package com.sunda.spmsoversea.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sunda.spmscommon.ResponseResult;
import com.sunda.spmscommon.feign.IFeignServiceCommon;
import com.sunda.spmsoversea.dto.*;
import com.sunda.spmsoversea.entity.*;
import com.sunda.spmsoversea.mapper.OverseaDnMapper;
import com.sunda.spmsoversea.service.*;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.sunda.spmscommon.constant.OperationTypeConstant.OVERSEA_GET_BOX_NOTE;
import static com.sunda.spmsoversea.constant.CommonConstant.SUCCESS;
import static com.sunda.spmsoversea.constant.CommonConstant.sapLog;
import static com.sunda.spmsoversea.constant.MovementTypeConstant.M643;
import static com.sunda.spmsoversea.constant.MovementTypeConstant.M644;
import static com.sunda.spmsoversea.constant.OverseaWhsInConstant.GET_BOX_NOTE;
import static com.sunda.spmsoversea.constant.SapConstant.*;
import static com.sunda.spmsoversea.constant.WhsStorageInventoryConstant.*;
import static com.sunda.spmsoversea.enums.OverseaDnEnum.*;

/**
 * <p>
 * 海外仓交货单表(空运人带/转储/本地采购) 服务实现类
 * </p>
 *
 * @author Wayne
 * @since 2021-12-14
 */
@Slf4j
@Service
public class OverseaDnServiceImpl extends ServiceImpl<OverseaDnMapper, OverseaDn> implements IOverseaDnService {

    @Autowired
    IFeignServiceCommon iFeignServiceCommon;

    @Autowired
    IFormatDataToSapService iFormatDataToSapService;

    @Autowired
    IOverseaDnDtlService iOverseaDnDtlService;

    @Autowired
    IOverseaPurchaseOrderService iOverseaPurchaseOrderService;

    @Autowired
    IOverseaPurchaseOrderDtlService iOverseaPurchaseOrderDtlService;

    @Autowired
    IWhsStorageInventoryService iWhsStorageInventoryService;

    @Autowired
    IWhsOperateLogService iWhsOperateLogService;

    @Autowired
    IOverseaOperateLogService iOverseaOperateLogService;

    /** 获取转储交货单表头分页列表 */
    @Override
    public IPage getDnPage(OverseaDnSearchDTO overseaDnSearchDTO) {
        IPage iPage = overseaDnSearchDTO.getPage();
        List<Map<String, Object>> mapList = this.baseMapper.getDnPage(iPage, overseaDnSearchDTO);
        iPage.setRecords(mapList);
        return iPage;
    }

    /** 根据交货单号获取交货单表头信息 */
    @Override
    public OverseaDn getDn(String sapDeliveryNote) {
        return this.baseMapper.selectOne(new QueryWrapper<OverseaDn>().eq("SAP_DELIVERY_NOTE", sapDeliveryNote));
    }

    /** 根据交货单号获取交货单表头详细信息 */
    @Override
    public Map<String, Object> getDnInfo(String sapDeliveryNote) {
        return this.baseMapper.getDnInfo(sapDeliveryNote);
    }

    /** 根据交货单号获取交货单明细信息 */
    @Override
    public List<Map<String, Object>> getDnDtlList(String sapDeliveryNote) {
        return iOverseaDnDtlService.getDnDtlList(sapDeliveryNote);
    }

    /** 确认到货，更新收货仓库，锁定DN、DN明细、PO、PO明细 四张表数据不再被更新；0-1； */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult confirmArrival(OverseaDnConfirmDTO overseaDnConfirmDTO, String userId) {
        try {
            if (overseaDnConfirmDTO == null){
                return ResponseResult.error("请求参数错误");
            }
            String sapDeliveryNote = overseaDnConfirmDTO.getSapDeliveryNote();
            String arrivalDate = overseaDnConfirmDTO.getArrivalDate();
            String sapPurchaseOrder = overseaDnConfirmDTO.getSapPurchaseOrder();
            String whsLocationCodeReceive = overseaDnConfirmDTO.getWhsLocationCodeReceive();
            Integer dataVersion = overseaDnConfirmDTO.getDataVersion();

            if (StringUtils.isEmpty(sapDeliveryNote) || StringUtils.isEmpty(sapPurchaseOrder) || dataVersion == null
                    || StringUtils.isEmpty(whsLocationCodeReceive) || StringUtils.isEmpty(arrivalDate)){
                return ResponseResult.error("请求参数错误");
            }
            QueryWrapper<OverseaDn> queryWrapper = new QueryWrapper<OverseaDn>().eq("SAP_DELIVERY_NOTE", sapDeliveryNote);
            OverseaDn overseaDn = getDn(sapDeliveryNote);
            if (overseaDn == null){
                return ResponseResult.error("查询交货单不存在");
            }
            if (!dataVersion.equals(overseaDn.getDataVersion())){
                return ResponseResult.error("交货单信息已更新请刷新后重试");
            }
            if (!DN_WAITING.getCode().equals(overseaDn.getSpmsStatus())){
                return ResponseResult.error("交货单当前状态禁止此操作");
            }
            overseaDn.setWhsLocationCodeReceive(whsLocationCodeReceive);
            overseaDn.setSpmsStatus(DN_RECEIVING.getCode());
            overseaDn.setArrivalDate(arrivalDate);
            overseaDn.setDataStatus(DATA_STATUS_1);
            this.baseMapper.update(overseaDn, queryWrapper);

            OverseaDnDtl overseaDnDtl = new OverseaDnDtl();
            QueryWrapper<OverseaDnDtl> dnDtlQueryWrapper = new QueryWrapper<OverseaDnDtl>().eq("SAP_DELIVERY_NOTE", sapDeliveryNote);
            overseaDnDtl.setWhsLocationCodeReceive(whsLocationCodeReceive);
            overseaDnDtl.setDataStatus(DATA_STATUS_1);
            iOverseaDnDtlService.update(overseaDnDtl, dnDtlQueryWrapper);

            lockOrUnlockDataStatus(sapPurchaseOrder, DATA_STATUS_1);

            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_RECEIVING.getDescZh(), DN_RECEIVING.getDescEn(), "");

            return ResponseResult.success("确认到货成功")
                    .add("overseaDn", getDnInfo(sapDeliveryNote))
                    .add("overseaDnDtlList", getDnDtlList(sapDeliveryNote));
        } catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("请求更新错误 Exception").add("error", e.getMessage());
        }
    }

    /** dataStatus = 0，解除数据锁定，SAP可更新PO数据；dataStatus = 1，锁定数据，SAP不再更新PO数据； */
    void lockOrUnlockDataStatus(String sapPurchaseOrder, String dataStatus){
        OverseaPurchaseOrder overseaPurchaseOrder = new OverseaPurchaseOrder();
        overseaPurchaseOrder.setDataStatus(dataStatus);
        QueryWrapper<OverseaPurchaseOrder> purchaseOrderQueryWrapper =
                new QueryWrapper<OverseaPurchaseOrder>().eq("SAP_PURCHASE_ORDER", sapPurchaseOrder);
        iOverseaPurchaseOrderService.update(overseaPurchaseOrder, purchaseOrderQueryWrapper);

        OverseaPurchaseOrderDtl overseaPurchaseOrderDtl = new OverseaPurchaseOrderDtl();
        overseaPurchaseOrderDtl.setDataStatus(dataStatus);
        QueryWrapper<OverseaPurchaseOrderDtl> purchaseOrderDtlQueryWrapper =
                new QueryWrapper<OverseaPurchaseOrderDtl>().eq("SAP_PURCHASE_ORDER", sapPurchaseOrder);
        iOverseaPurchaseOrderDtlService.update(overseaPurchaseOrderDtl, purchaseOrderDtlQueryWrapper);
    }

    /** 交货单关闭成废单；0-10；1-10； */
    @Override
    public synchronized ResponseResult closeDn(String sapDeliveryNote, Integer dataVersion, String userId) {
        if (StringUtils.isEmpty(sapDeliveryNote) || dataVersion == null){
            return ResponseResult.error("请求参数错误");
        }
        QueryWrapper<OverseaDn> queryWrapper = new QueryWrapper<OverseaDn>().eq("SAP_DELIVERY_NOTE", sapDeliveryNote);
        OverseaDn overseaDn = getDn(sapDeliveryNote);
        if (overseaDn == null){
            return ResponseResult.error("查询交货单不存在");
        }
        if (!dataVersion.equals(overseaDn.getDataVersion())){
            return ResponseResult.error("交货单信息已更新请刷新后重试");
        }
        if (!DN_WAITING.getCode().equals(overseaDn.getSpmsStatus()) &&
                !DN_RECEIVING.getCode().equals(overseaDn.getSpmsStatus())){
            return ResponseResult.error("交货单当前状态禁止此操作");
        }
        overseaDn.setSpmsStatus(DN_DELETE.getCode());
        overseaDn.setDataStatus(DATA_STATUS_1);
        this.baseMapper.update(overseaDn, queryWrapper);
        iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_DELETE.getDescZh(), DN_DELETE.getDescEn(), "");

        return ResponseResult.success("交货单关闭成功")
                .add("overseaDn", getDnInfo(sapDeliveryNote));
    }

    /** 交货单 保存 1-1、入库完成/提交审批 1-2、审批通过 2-3(SPMS按实收入库加库存) */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult updateDn(OverseaDnUpdateDTO overseaDnUpdateDTO, String userId) {
        try {
            ResponseResult checkResult = checkUpdateDn(overseaDnUpdateDTO);
            if (checkResult.getCode() != SUCCESS){
                return checkResult;
            }
            /** 空运人带/转储/本地采购————更新交货单表头 */
            String sapDeliveryNote = overseaDnUpdateDTO.getSapDeliveryNote();
            String targetStatus = overseaDnUpdateDTO.getSpmsStatus();
            OverseaDn overseaDn = getDn(sapDeliveryNote);
            overseaDn.setSpmsStatus(targetStatus);
            if (DN_WHS_IN_DONE.getCode().equals(targetStatus)){
                overseaDn.setOperateUser(userId);
            }
            overseaDn.setRemarks(StringUtils.isEmpty(overseaDn.getRemarks()) ? "-" : overseaDn.getRemarks());
            overseaDn.setCostCenter(overseaDn.getCostCenter());
            this.baseMapper.update(overseaDn, new QueryWrapper<OverseaDn>().eq("SAP_DELIVERY_NOTE", sapDeliveryNote));

            /** 空运人带/转储/本地采购————更新交货单明细 */
            List<OverseaDnDtlUpdateDTO> dnDtlItem = overseaDnUpdateDTO.getOverseaDnDtlItem();
            for (OverseaDnDtlUpdateDTO dnDtl : dnDtlItem){
                String sapDeliveryNoteItem = dnDtl.getSapDeliveryNoteItem();
                QueryWrapper<OverseaDnDtl> queryWrapper = new QueryWrapper<OverseaDnDtl>().eq("SAP_DELIVERY_NOTE",
                        sapDeliveryNote).eq("SAP_DELIVERY_NOTE_ITEM", sapDeliveryNoteItem);
                OverseaDnDtl overseaDnDtl = new OverseaDnDtl();
                overseaDnDtl.setActualQtyBasicUnit(dnDtl.getActualQtyBasicUnit());
                overseaDnDtl.setActualQtyUnitSales(dnDtl.getActualQtyUnitSales());
                overseaDnDtl.setRemarks(StringUtils.isEmpty(dnDtl.getRemarks()) ? "-" : dnDtl.getRemarks());
                overseaDnDtl.setStorageInfo(dnDtl.getStorageInfo());
                iOverseaDnDtlService.update(overseaDnDtl, queryWrapper);
            }

            /** 空运人带/转储/本地采购————海外收货 审批通过 增加库存 */
            if (DN_APPROVED.getCode().equals(targetStatus)){
                ResponseResult updateStorageInventory = updateWhsStorageInventory(sapDeliveryNote, ADD, userId, OPERATION_TYPE2,
                        overseaDn.getWerksReceive(), overseaDn.getWhsLocationCodeReceive(), M643, "转储收货入库");
                if (updateStorageInventory.getCode() != SUCCESS){
                    log.info("=======RuntimeException=========\n" + updateStorageInventory.getMsg());
                    throw new RuntimeException("储位库存更新错误\n" + updateStorageInventory.getMsg());
                }
            }

            /** 空运人带/转储/本地采购————记录箱码海外到货日志，一个箱码多个明细，只记录一次到货 */
            addWhsOperateLog(iOverseaDnDtlService.getDnDtls(sapDeliveryNote), userId);

            if (DN_RECEIVING.getCode().equals(targetStatus)){
                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_RECEIVING.getDescZh(), DN_RECEIVING.getDescEn(), "");
            }
            if (DN_WHS_IN_DONE.getCode().equals(targetStatus)){
                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_WHS_IN_DONE.getDescZh(), DN_WHS_IN_DONE.getDescEn(), "");
            }
            if (DN_APPROVED.getCode().equals(targetStatus)) {
                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_APPROVED.getDescZh(), DN_APPROVED.getDescEn(), "");
            }
            return ResponseResult.success("确认到货成功")
                    .add("overseaDn", getDnInfo(sapDeliveryNote))
                    .add("overseaDnDtlList", getDnDtlList(sapDeliveryNote));
        } catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("请求更新错误 Exception").add("error", e.getMessage());
        }
    }

    /** 记录箱码海外到货日志，一个箱码多个明细，只记录一次到货 */
    void addWhsOperateLog(List<OverseaDnDtl> overseaDnDtlList, String userId){
        Set set = new HashSet();
        for (OverseaDnDtl overseaDnDtl : overseaDnDtlList){
            String boxNote = overseaDnDtl.getBoxNote();
            if (overseaDnDtl.getActualQtyBasicUnit() > 0 && StringUtils.isNotEmpty(boxNote) && !set.contains(boxNote)){
                String sapDeliveryNote = overseaDnDtl.getSapDeliveryNote();
                String werks = overseaDnDtl.getWerksReceive();
                String whsLocationCode = overseaDnDtl.getWhsLocationCodeReceive();
                iWhsOperateLogService.recordGetBoxNote(boxNote, OVERSEA_GET_BOX_NOTE, sapDeliveryNote, sapDeliveryNote, GET_BOX_NOTE,
                        userId, "", werks, whsLocationCode, M643);
                set.add(boxNote);
            }
        }
    }

    /** 交货单 保存 1-1、入库完成/提交审批 1-2、审批通过 2-3(SPMS按实收入库加库存) */
    ResponseResult checkUpdateDn (OverseaDnUpdateDTO overseaDnUpdateDTO){
        String sapDeliveryNote = overseaDnUpdateDTO.getSapDeliveryNote();
        String targetStatus = overseaDnUpdateDTO.getSpmsStatus();
        Integer dataVersion = overseaDnUpdateDTO.getDataVersion();
        if (StringUtils.isEmpty(sapDeliveryNote) || StringUtils.isEmpty(targetStatus) || dataVersion == null){
            return ResponseResult.error("请求参数错误");
        }
        OverseaDn overseaDn = getDn(sapDeliveryNote);
        if (overseaDn == null){
            return ResponseResult.error("查询交货单不存在");
        }
        if (!dataVersion.equals(overseaDn.getDataVersion())){
            return ResponseResult.error("交货单信息已更新请刷新后重试");
        }
        String spmsStatus = overseaDn.getSpmsStatus();
        if (spmsStatus.equals(DN_RECEIVING.getCode())){
            if (!DN_RECEIVING.getCode().equals(targetStatus)
                    && !DN_WHS_IN_DONE.getCode().equals(targetStatus)){
                return ResponseResult.error("交货单目标状态错误");
            }
        } else if (spmsStatus.equals(DN_WHS_IN_DONE.getCode())){
            if (!DN_APPROVED.getCode().equals(targetStatus)){
                return ResponseResult.error("交货单目标状态错误");
            }
        } else {
            return ResponseResult.error("交货单当前状态禁止此操作");
        }
        List<OverseaDnDtlUpdateDTO> dnDtlItem = overseaDnUpdateDTO.getOverseaDnDtlItem();
        if (dnDtlItem == null || dnDtlItem.size() == 0){
            return ResponseResult.error("请求参数交货单明细不能为空");
        }
        return ResponseResult.success();
    }


    synchronized ResponseResult updateWhsStorageInventory(String sapDeliveryNote, String operateType, String userId, String logType, String werks, String whsLocationCode, String movementType, String remark){
        List<OverseaDnDtl> overseaDnDtlList = iOverseaDnDtlService.getDnDtls(sapDeliveryNote);
        if (overseaDnDtlList == null || overseaDnDtlList.size() == 0){
            return ResponseResult.error("入库任务明细数据查询不存在");
        }
        for (OverseaDnDtl overseaDnDtl : overseaDnDtlList){
            String materialNo = overseaDnDtl.getMaterialNo();
            double actualQtyBasicUnit = overseaDnDtl.getActualQtyBasicUnit() == null ? 0 : overseaDnDtl.getActualQtyBasicUnit();
            if (actualQtyBasicUnit > 0){
                //storageInfoArray 结构参考：[{"storageUuid":"f17e13b4d9ad4d39ba55548eff544391","qty":100,"storageNo":"C010101"}]
                JSONArray storageInfoArray = JSONArray.parseArray(overseaDnDtl.getStorageInfo());
                for (int i = 0; i < storageInfoArray.size(); i ++){
                    JSONObject storageInfoObj = storageInfoArray.getJSONObject(i);
                    String storageUuid = storageInfoObj.getString("storageUuid");
                    double qty = storageInfoObj.getDoubleValue("qty");
                    if (qty > 0){
                        ResponseResult updateInventory = iWhsStorageInventoryService.updateWhsStorageInventory(storageUuid, materialNo, qty, operateType);
                        if (updateInventory.getCode() != SUCCESS){
                            return updateInventory;
                        }
                        String comments = operateType.equals(ADD) ? WHS_OPERATE_LOG_ADD : WHS_OPERATE_LOG_SUB;
                        iWhsOperateLogService.insertWhsOperateLog(storageUuid, materialNo, qty, logType, sapDeliveryNote, comments, userId, remark, werks, whsLocationCode, movementType);
                    }
                }
            }
        }
        return ResponseResult.success();
    }

    /**
     3审批通过/5按应收提交SAP失败
        提交SAP —— 按应收数量提交SAP
            失败 —— 审批通过后点击提交SAP失败 3-5/5-5；
            成功
                i.不存在少收 3-4-8-9/5-4-8-9；
                ii.存在少收，系统自动提交少收
                    少收提交成功 3-4-7-8-9/5-4-7-8-9；
                    少收提交失败 3-4-6/5-4-6；
     4按应收提交SAP成功/6收货少收提交SAP失败
        提交SAP
            i.不存在少收
                4-8-9；
            ii.存在少收，则将少收信息重新提交SAP
                成功 4-7-8-9/6-7-8-9；
                失败 4-6/6-6；
     本地采购提交SAP单独一套逻辑，按实收提交，无多收少收：
        3审批通过/5按实收提交SAP失败
            提交SAP —— 按实收数量提交SAP
                 失败 —— 审批通过后点击提交SAP失败 3-5/5-5；
                 成功 —— 3-8-9/5-8-9；
     * */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult submitDnSap(OverseaDnSapDTO overseaDnSapDTO, String userId) {
        try {
            ResponseResult checkResult = checkSubmitSap(overseaDnSapDTO);
            if (checkResult.getCode() != SUCCESS){
                return checkResult;
            }
            String sapDeliveryNote = overseaDnSapDTO.getSapDeliveryNote();
            String postingDate = overseaDnSapDTO.getPostingDate();
            String underPostingDate = overseaDnSapDTO.getUnderPostingDate();
            String costCenter = overseaDnSapDTO.getCostCenter() == null ? "" : overseaDnSapDTO.getCostCenter();

            QueryWrapper<OverseaDn> queryWrapper = new QueryWrapper<OverseaDn>().eq("SAP_DELIVERY_NOTE", sapDeliveryNote);
            OverseaDn overseaDn = this.baseMapper.selectOne(queryWrapper);
            String businessType = overseaDn.getBusinessType();
            String spmsStatus = overseaDn.getSpmsStatus();
            // 02——本地采购，提交SAP时，按实收提交SAP即可，允许多收少收不单独提交SAP；不需要成本中心字段；
            if ("02".equals(businessType)){
                if (spmsStatus.equals(DN_APPROVED.getCode()) || spmsStatus.equals(DN_SUBMIT_SAP_ERROR.getCode())){
                    Map<String, Object> dataHead = this.baseMapper.dataHead(sapDeliveryNote, postingDate, userId);
                    List<Map<String, Object>> dataItems = this.baseMapper.actualDataItems(sapDeliveryNote);
                    if (dataHead != null && dataItems != null && dataItems.size() > 0){
                        JSONObject dataToSap = iFormatDataToSapService.formatDataToSap(dataHead, dataItems, "Z_SPMS_MIGO", "SPMS_MIGO");
                        log.info("=======海外本地采购交货单按 实收收货入库请求SAP dataToSap=========\n" + dataToSap);
                        JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(dataToSap));
                        log.info("=======海外本地采购交货单按 实收收货入库请求SAP message=========\n" + message);
                        JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                        if (SAP_SUCCESS.equals(RETURN_DATA.getString("O_TYPE"))) {
                            String sapVoucherNumber = RETURN_DATA.getJSONArray("IT_MSG").getJSONObject(0).getString("MBLNR");
                            String sapVoucherYear = RETURN_DATA.getJSONArray("IT_MSG").getJSONObject(0).getString("MJAHR");

                            overseaDn.setSpmsStatus(DN_SUBMIT_SAP_DONE.getCode());
                            overseaDn.setPostingDate(postingDate);
                            overseaDn.setSapVoucherNumber(sapVoucherNumber);
                            overseaDn.setSapVoucherYear(sapVoucherYear);
                            this.baseMapper.update(overseaDn, queryWrapper);
                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_SUBMIT_SAP_DONE.getDescZh() + ":" + sapVoucherNumber,
                                    DN_SUBMIT_SAP_DONE.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "按应收提交SAP成功");

                            overseaDn.setSpmsStatus(DN_COMPLETED.getCode());
                            this.baseMapper.update(overseaDn, queryWrapper);
                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_COMPLETED.getDescZh(), DN_COMPLETED.getDescEn(), "");

                            return ResponseResult.success("交货单完成收货提交SAP成功")
                                    .add("overseaDn", getDnInfo(sapDeliveryNote));
                        } else {
                            overseaDn.setSpmsStatus(DN_SUBMIT_SAP_ERROR1.getCode());
                            overseaDn.setPostingDate(postingDate);
                            this.baseMapper.update(overseaDn, queryWrapper);
                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_SUBMIT_SAP_ERROR1.getDescZh(),
                                    DN_SUBMIT_SAP_ERROR1.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "按实收提交SAP失败");
                            return ResponseResult.error("交货单完成收货提交SAP错误")
                                    .add("sapReturnedMessage", message)
                                    .add("dataToSap", dataToSap)
                                    .add("overseaDn", getDnInfo(sapDeliveryNote));
                        }
                    } else {
                        return ResponseResult.error("交货单号" + sapDeliveryNote + "获取提交SAP信息结构错误");
                    }
                } else {
                    return ResponseResult.error("本地采购交货单状态错误");
                }
            }
            if ("01".equals(businessType) || "03".equals(businessType)){
                if (spmsStatus.equals(DN_APPROVED.getCode()) || spmsStatus.equals(DN_SUBMIT_SAP_ERROR.getCode())){
                    Map<String, Object> dataHead = this.baseMapper.dataHead(sapDeliveryNote, postingDate, userId);
                    List<Map<String, Object>> dataItems = this.baseMapper.dataItems(sapDeliveryNote);
                    if (dataHead != null && dataItems != null && dataItems.size() > 0){
                        JSONObject dataToSap = iFormatDataToSapService.formatDataToSap(dataHead, dataItems, "Z_SPMS_MIGO", "SPMS_MIGO");
                        log.info("=======海外转储收货交货单按应收收货入库请求SAP dataToSap=========\n" + dataToSap);
                        JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(dataToSap));
                        log.info("=======海外转储收货交货单按应收收货入库请求SAP message=========\n" + message);
                        JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                        if (SAP_SUCCESS.equals(RETURN_DATA.getString("O_TYPE"))) {
                            String sapVoucherNumber = RETURN_DATA.getJSONArray("IT_MSG").getJSONObject(0).getString("MBLNR");
                            String sapVoucherYear = RETURN_DATA.getJSONArray("IT_MSG").getJSONObject(0).getString("MJAHR");

                            overseaDn.setSpmsStatus(DN_SUBMIT_SAP.getCode());
                            overseaDn.setPostingDate(postingDate);
                            overseaDn.setSapVoucherNumber(sapVoucherNumber);
                            overseaDn.setSapVoucherYear(sapVoucherYear);
                            this.baseMapper.update(overseaDn, queryWrapper);

                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_SUBMIT_SAP.getDescZh() + ":" + sapVoucherNumber,
                                    DN_SUBMIT_SAP.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "按应收提交SAP成功");

                            /** 按应收入库提交SAP成功后，检查是否存在 少收行，存在则将少收提交SAP；不存在则更新状态记录日志； */
                            List<Map<String, Object>> dataItemsUndercharged = this.baseMapper.dataItemsAnomalyPost(sapDeliveryNote, costCenter);
                            if (dataItemsUndercharged != null && dataItemsUndercharged.size() > 0){
                                Map<String, Object> dataHeadUndercharged = this.baseMapper.dataHeadAnomalyPost(sapDeliveryNote, underPostingDate);
                                JSONObject dataToSapUndercharged = iFormatDataToSapService.formatDataToSap(dataHeadUndercharged, dataItemsUndercharged, "Z_SPMS_ANOMALY_POST", "ANOMALY_POST");
                                log.info("=======海外转储收货交货单少收成本中心入库请求SAP dataToSap=========\n" + dataToSapUndercharged);
                                JSONObject messageUndercharged = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(dataToSapUndercharged));
                                log.info("=======海外转储收货交货单少收成本中心入库请求SAP响应结果 message=========\n" + messageUndercharged);
                                JSONObject RETURN_DATA_Undercharged = messageUndercharged.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                                if (SAP_SUCCESS.equals(RETURN_DATA_Undercharged.getString("O_TYPE"))){
                                    String underVoucherNumber = RETURN_DATA_Undercharged.getString("O_MBLNR").trim();
                                    String underVoucherYear = RETURN_DATA_Undercharged.getString("O_MJAHR").trim();

                                    overseaDn.setUnderPostingDate(underPostingDate);
                                    overseaDn.setCostCenter(costCenter);
                                    overseaDn.setUnderVoucherNumber(underVoucherNumber);
                                    overseaDn.setUnderVoucherYear(underVoucherYear);
                                    overseaDn.setSpmsStatus(DN_UNDERCHARGED_TO_SAP.getCode());
                                    this.baseMapper.update(overseaDn, queryWrapper);

                                    iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_UNDERCHARGED_TO_SAP.getDescZh() + ":" + underVoucherNumber,
                                            DN_UNDERCHARGED_TO_SAP.getDescEn(), sapLog, dataToSapUndercharged.toJSONString(), messageUndercharged.toJSONString(), "收货少收提交SAP成功");

                                    overseaDn.setSpmsStatus(DN_SUBMIT_SAP_DONE.getCode());
                                    this.baseMapper.update(overseaDn, queryWrapper);
                                    iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_SUBMIT_SAP_DONE.getDescZh(), DN_SUBMIT_SAP_DONE.getDescEn(), "");

                                    overseaDn.setSpmsStatus(DN_COMPLETED.getCode());
                                    this.baseMapper.update(overseaDn, queryWrapper);
                                    iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_COMPLETED.getDescZh(), DN_COMPLETED.getDescEn(), "");

                                } else {
                                    overseaDn.setCostCenter(costCenter);
                                    overseaDn.setUnderPostingDate(underPostingDate);
                                    overseaDn.setSpmsStatus(DN_UNDERCHARGED_TO_SAP_ERROR.getCode());
                                    this.baseMapper.update(overseaDn, queryWrapper);

                                    iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_UNDERCHARGED_TO_SAP_ERROR.getDescZh(),
                                            DN_UNDERCHARGED_TO_SAP_ERROR.getDescEn(), sapLog, dataToSapUndercharged.toJSONString(), messageUndercharged.toJSONString(), "收货少收提交SAP失败");

                                    return ResponseResult.error("交货单完成收货提交SAP完成，少收部分提交SAP错误")
                                            .add("sapReturnedMessage", messageUndercharged)
                                            .add("dataToSap", dataToSapUndercharged)
                                            .add("overseaDn", getDnInfo(sapDeliveryNote));

                                }
                            } else {
                                overseaDn.setSpmsStatus(DN_SUBMIT_SAP_DONE.getCode());
                                this.baseMapper.update(overseaDn, queryWrapper);
                                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_SUBMIT_SAP_DONE.getDescZh(), DN_SUBMIT_SAP_DONE.getDescEn(), "");

                                overseaDn.setSpmsStatus(DN_COMPLETED.getCode());
                                this.baseMapper.update(overseaDn, queryWrapper);
                                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_COMPLETED.getDescZh(), DN_COMPLETED.getDescEn(), "");
                            }

                            return ResponseResult.success("交货单完成收货提交SAP成功")
                                    .add("overseaDn", getDnInfo(sapDeliveryNote));
                        } else {
                            overseaDn.setSpmsStatus(DN_SUBMIT_SAP_ERROR.getCode());
                            overseaDn.setPostingDate(postingDate);
                            this.baseMapper.update(overseaDn, queryWrapper);
                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_SUBMIT_SAP_ERROR.getDescZh(),
                                    DN_SUBMIT_SAP_ERROR.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "按应收提交SAP失败");
                            return ResponseResult.error("交货单完成收货提交SAP错误")
                                    .add("sapReturnedMessage", message)
                                    .add("dataToSap", dataToSap)
                                    .add("overseaDn", getDnInfo(sapDeliveryNote));
                        }
                    } else {
                        return ResponseResult.error("交货单号" + sapDeliveryNote + "获取提交SAP信息结构错误");
                    }
                }
                if (spmsStatus.equals(DN_SUBMIT_SAP.getCode()) || spmsStatus.equals(DN_UNDERCHARGED_TO_SAP_ERROR.getCode())){
                    List<Map<String, Object>> dataItemsUndercharged = this.baseMapper.dataItemsAnomalyPost(sapDeliveryNote, costCenter);
                    if (dataItemsUndercharged != null && dataItemsUndercharged.size() > 0){
                        Map<String, Object> dataHeadUndercharged = this.baseMapper.dataHeadAnomalyPost(sapDeliveryNote, underPostingDate);
                        JSONObject dataToSapUndercharged = iFormatDataToSapService.formatDataToSap(dataHeadUndercharged, dataItemsUndercharged, "Z_SPMS_ANOMALY_POST", "ANOMALY_POST");
                        log.info("=======海外转储收货交货单少收成本中心入库请求SAP dataToSap=========\n" + dataToSapUndercharged);
                        JSONObject messageUndercharged = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(dataToSapUndercharged));
                        log.info("=======海外转储收货交货单少收成本中心入库请求SAP响应结果 message=========\n" + messageUndercharged);
                        JSONObject RETURN_DATA_Undercharged = messageUndercharged.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                        if (SAP_SUCCESS.equals(RETURN_DATA_Undercharged.getString("O_TYPE"))) {
                            String underVoucherNumber = RETURN_DATA_Undercharged.getString("O_MBLNR").trim();
                            String underVoucherYear = RETURN_DATA_Undercharged.getString("O_MJAHR").trim();

                            overseaDn.setUnderPostingDate(underPostingDate);
                            overseaDn.setCostCenter(costCenter);
                            overseaDn.setUnderVoucherNumber(underVoucherNumber);
                            overseaDn.setUnderVoucherYear(underVoucherYear);
                            overseaDn.setSpmsStatus(DN_UNDERCHARGED_TO_SAP.getCode());
                            this.baseMapper.update(overseaDn, queryWrapper);

                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_UNDERCHARGED_TO_SAP.getDescZh() + ":" + underVoucherNumber,
                                    DN_UNDERCHARGED_TO_SAP.getDescEn(), sapLog, dataToSapUndercharged.toJSONString(), messageUndercharged.toJSONString(), "收货少收提交SAP成功");

                            overseaDn.setSpmsStatus(DN_SUBMIT_SAP_DONE.getCode());
                            this.baseMapper.update(overseaDn, queryWrapper);
                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_SUBMIT_SAP_DONE.getDescZh(), DN_SUBMIT_SAP_DONE.getDescEn(), "");

                            overseaDn.setSpmsStatus(DN_COMPLETED.getCode());
                            this.baseMapper.update(overseaDn, queryWrapper);
                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_COMPLETED.getDescZh(), DN_COMPLETED.getDescEn(), "");
                        } else {
                            overseaDn.setCostCenter(costCenter);
                            overseaDn.setUnderPostingDate(underPostingDate);
                            overseaDn.setSpmsStatus(DN_UNDERCHARGED_TO_SAP_ERROR.getCode());
                            this.baseMapper.update(overseaDn, queryWrapper);

                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_UNDERCHARGED_TO_SAP_ERROR.getDescZh(),
                                    DN_UNDERCHARGED_TO_SAP_ERROR.getDescEn(), sapLog, dataToSapUndercharged.toJSONString(), messageUndercharged.toJSONString(), "收货少收提交SAP失败");

                            return ResponseResult.error("交货单完成收货提交SAP完成，少收部分提交SAP错误")
                                    .add("sapReturnedMessage", messageUndercharged)
                                    .add("dataToSap", dataToSapUndercharged)
                                    .add("overseaDn", getDnInfo(sapDeliveryNote));
                        }
                    } else {
                        overseaDn.setSpmsStatus(DN_SUBMIT_SAP_DONE.getCode());
                        this.baseMapper.update(overseaDn, queryWrapper);
                        iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_SUBMIT_SAP_DONE.getDescZh(), DN_SUBMIT_SAP_DONE.getDescEn(), "");

                        overseaDn.setSpmsStatus(DN_COMPLETED.getCode());
                        this.baseMapper.update(overseaDn, queryWrapper);
                        iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_COMPLETED.getDescZh(), DN_COMPLETED.getDescEn(), "");
                    }
                    return ResponseResult.success("交货单完成收货提交SAP成功")
                            .add("overseaDn", getDnInfo(sapDeliveryNote));
                }
            }
            return ResponseResult.error("交货单业务类型错误");
        }catch (FeignException feignException){
            feignException.printStackTrace();
            return ResponseResult.error("请求SAP失败 FeignException").add("error", feignException.getMessage());
        }catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("收货完成更新单据或提交SAP失败 Exception").add("error", e.getMessage());
        }
    }

    ResponseResult checkSubmitSap(OverseaDnSapDTO overseaDnSapDTO){
        if (overseaDnSapDTO == null){
            return ResponseResult.error("请求参数错误");
        }
        String sapDeliveryNote = overseaDnSapDTO.getSapDeliveryNote();
        String postingDate = overseaDnSapDTO.getPostingDate();
        String underPostingDate = overseaDnSapDTO.getUnderPostingDate();
        Integer dataVersion = overseaDnSapDTO.getDataVersion();
        String costCenter = overseaDnSapDTO.getCostCenter();
        if (StringUtils.isEmpty(sapDeliveryNote) || dataVersion == null){
            return ResponseResult.error("请求参数错误");
        }
//        if (StringUtils.isEmpty(costCenter)){
//            return ResponseResult.error("成本中心不允许为空");
//        }
        OverseaDn overseaDn = getDn(sapDeliveryNote);
        if (overseaDn == null){
            return ResponseResult.error("请求交货单不存在");
        }
        if (!dataVersion.equals(overseaDn.getDataVersion())){
            return ResponseResult.error("请求交货单数据已更新，请刷新后重试");
        }
        if (overseaDn.getBusinessType() == null){
            return ResponseResult.error("请求交货单业务类型为空");
        }
        String spmsStatus = overseaDn.getSpmsStatus();
        if (spmsStatus.equals(DN_APPROVED.getCode()) || spmsStatus.equals(DN_SUBMIT_SAP_ERROR.getCode())){
            if (StringUtils.isEmpty(postingDate)){
                return ResponseResult.error("收货过账日期不允许为空");
            }
        } else if (spmsStatus.equals(DN_SUBMIT_SAP.getCode()) || spmsStatus.equals(DN_UNDERCHARGED_TO_SAP_ERROR.getCode())){
            if (StringUtils.isEmpty(underPostingDate)){
                return ResponseResult.error("少收收货过账日期不允许为空");
            }
        } else {
            return ResponseResult.error("当前状态禁止此操作");
        }
        return ResponseResult.success();
    }

    /***
     交货单撤销功能：
     0待到货;
     1入库中/审批不通过;
        撤回 —— 撤回到待到货 1-0；
     2入库完成/待审批;
        审批不通过 —— 退回到入库中 2-1；
     3审批通过/SPMS按实收入库存;
        撤回 —— 撤回到入库中，SPMS按实收撤回入库减库存 3-1；
     4按应收提交SAP成功;
        撤回 —— 按应收提交的入库凭证从SAP撤销
             撤回成功 4-3；
             撤回失败 4-4；
     5按应收提交SAP失败;
        撤回 —— 撤回到入库中，SPMS按实收撤回入库减库存 5-1；
     6收货少收提交SAP失败;
        撤回 —— 按应收提交的入库凭证从SAP撤销
             撤回成功 6-3；
             撤回失败 6-6；
     7收货少收提交SAP成功; —— 中间状态
     8提交SAP完成; —— 中间状态
     9收货完成/完结状态;
        不存在少收，撤回则按应收入库凭证从SAP撤销
             成功 9-3；
             失败 9-9；
        存在少收，先从SAP撤回少收凭证
             失败 9-9；
             成功 9-4；
                从SAP撤回按应收提交的入库凭证
                     成功 4-3；
                     失败 4-4；
     10关闭/废单;
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult cancelDn(OverseaDnSapDTO overseaDnSapDTO, String userId) {
        try {
            ResponseResult checkCancelResult = checkCancel(overseaDnSapDTO);
            if (checkCancelResult.getCode() != SUCCESS){
                return checkCancelResult;
            }
            String sapDeliveryNote = overseaDnSapDTO.getSapDeliveryNote();
            String cancelPostingDate = overseaDnSapDTO.getCancelPostingDate();
            String cancelUnderPostingDate = overseaDnSapDTO.getCancelUnderPostingDate();

            QueryWrapper<OverseaDn> queryWrapper = new QueryWrapper<OverseaDn>().eq("SAP_DELIVERY_NOTE", sapDeliveryNote);
            OverseaDn overseaDn = this.baseMapper.selectOne(queryWrapper);
            String spmsStatus = overseaDn.getSpmsStatus();
            String businessType = overseaDn.getBusinessType();
            if (DN_RECEIVING.getCode().equals(spmsStatus)){
                overseaDn.setSpmsStatus(DATA_STATUS_0);
                this.baseMapper.update(overseaDn, queryWrapper);
                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_TO_WAITING.getDescZh(), DN_CANCEL_TO_WAITING.getDescEn(), "");

                QueryWrapper<OverseaDnDtl> dnDtlQueryWrapper = new QueryWrapper<OverseaDnDtl>().eq("SAP_DELIVERY_NOTE", sapDeliveryNote);
                OverseaDnDtl overseaDnDtl = new OverseaDnDtl();
                overseaDnDtl.setDataStatus(DATA_STATUS_0);
                iOverseaDnDtlService.update(overseaDnDtl, dnDtlQueryWrapper);

                List<OverseaDnDtl> overseaDnDtlList = iOverseaDnDtlService.getDnDtls(sapDeliveryNote);
                String sapPurchaseOrder = overseaDnDtlList.get(0).getSapPurchaseOrder();
                lockOrUnlockDataStatus(sapPurchaseOrder, DATA_STATUS_0);

                return ResponseResult.success("撤销到待到货，交货单已更新")
                        .add("overseaDn", getDnInfo(sapDeliveryNote))
                        .add("overseaDnDtlList", getDnDtlList(sapDeliveryNote));
            }
            if (DN_WHS_IN_DONE.getCode().equals(spmsStatus)){
                overseaDn.setSpmsStatus(DN_REJECT.getCode());
                this.baseMapper.update(overseaDn, queryWrapper);

                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_REJECT.getDescZh(), DN_REJECT.getDescEn(), "");
                return ResponseResult.success("审批不通过，交货单已更新").add("overseaDn", getDnInfo(sapDeliveryNote));
            }
            if (DN_APPROVED.getCode().equals(spmsStatus) || DN_SUBMIT_SAP_ERROR.getCode().equals(spmsStatus)){
                ResponseResult updateInventory = updateWhsStorageInventory(sapDeliveryNote, SUB, userId, OPERATION_TYPE3, overseaDn.getWerksReceive(), overseaDn.getWhsLocationCodeReceive(), M644, "转储入库撤销");
                if (updateInventory.getCode() != SUCCESS){
                    log.info("=======RuntimeException=========\n" + updateInventory.getMsg());
                    throw new RuntimeException("储位库存更新错误\n" + updateInventory.getMsg());
                }
                overseaDn.setSpmsStatus(DN_CANCEL_TO_RECEIVING.getCode());
                this.baseMapper.update(overseaDn, queryWrapper);

                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_TO_RECEIVING.getDescZh(), DN_CANCEL_TO_RECEIVING.getDescEn(), "");
                return ResponseResult.success("撤销到收货中，交货单已更新").add("overseaDn", getDnInfo(sapDeliveryNote));
            }
            if (DN_SUBMIT_SAP.getCode().equals(spmsStatus) || DN_UNDERCHARGED_TO_SAP_ERROR.getCode().equals(spmsStatus)){
                Map<String, Object> dataHead = this.baseMapper.dataHeadCancelMigo(sapDeliveryNote, cancelPostingDate);
                JSONObject dataToSap = iFormatDataToSapService.formatDataToSap(dataHead, null, "Z_SPMS_CANCEL", "SPMS_CANCEL");
                log.info("=======海外转储收货交货单入库撤销请求SAP dataToSap=========\n" + dataToSap);
                JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(dataToSap));
                log.info("=======海外转储收货交货单入库撤销请求SAP响应结果 message=========\n" + message);
                JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                if (SAP_SUCCESS.equals(RETURN_DATA.getString("O_TYPE"))){
                    String cancelVoucherNumber = RETURN_DATA.getString("0_MBLNR");
                    overseaDn.setCancelPostingDate(cancelPostingDate);
                    overseaDn.setCancelVoucherNumber(cancelVoucherNumber);
                    overseaDn.setSpmsStatus(DN_CANCEL_TO_APPROVED.getCode());
                    this.baseMapper.update(overseaDn, queryWrapper);

                    iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_TO_APPROVED.getDescZh() + ":" + cancelVoucherNumber,
                            DN_CANCEL_TO_APPROVED.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "交货单入库撤销提交SAP");
                    return ResponseResult.success("撤销到审批通过，交货单已更新").add("overseaDn", getDnInfo(sapDeliveryNote));
                } else {
                    overseaDn.setCancelPostingDate(cancelPostingDate);
                    this.baseMapper.update(overseaDn, queryWrapper);

                    iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_ERROR.getDescZh(),
                            DN_CANCEL_ERROR.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "交货单入库撤销失败");
                    return ResponseResult.error("交货单入库撤销失败")
                            .add("overseaDn", getDnInfo(sapDeliveryNote))
                            .add("sapReturnedMessage", message)
                            .add("dataToSap", dataToSap);
                }
            }
            if (DN_COMPLETED.getCode().equals(spmsStatus)){
                /** 02——本地采购，提交SAP时，按实收提交SAP即可，多收少收不单独提交SAP；撤销状态变化 9-3，直接撤销凭证即可 */
                if ("02".equals(businessType)){
                    Map<String, Object> dataHead = this.baseMapper.dataHeadCancelMigo(sapDeliveryNote, cancelPostingDate);
                    JSONObject dataToSap = iFormatDataToSapService.formatDataToSap(dataHead, null, "Z_SPMS_CANCEL", "SPMS_CANCEL");
                    log.info("=======海外本地采购 交货单入库撤销请求SAP dataToSap=========\n" + dataToSap);
                    JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(dataToSap));
                    log.info("=======海外本地采购 交货单入库撤销请求SAP响应结果 message=========\n" + message);
                    JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                    if (SAP_SUCCESS.equals(RETURN_DATA.getString("O_TYPE"))){
                        String cancelVoucherNumber = RETURN_DATA.getString("0_MBLNR");
                        overseaDn.setCancelPostingDate(cancelPostingDate);
                        overseaDn.setCancelVoucherNumber(cancelVoucherNumber);
                        overseaDn.setSpmsStatus(DN_CANCEL_TO_APPROVED.getCode());
                        this.baseMapper.update(overseaDn, queryWrapper);

                        iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_TO_APPROVED.getDescZh() + ":" + cancelVoucherNumber,
                                DN_CANCEL_TO_APPROVED.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "交货单入库撤销提交SAP");
                        return ResponseResult.success("撤销到审批通过，交货单已更新").add("overseaDn", getDnInfo(sapDeliveryNote));
                    } else {
                        overseaDn.setCancelPostingDate(cancelPostingDate);
                        this.baseMapper.update(overseaDn, queryWrapper);

                        iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_ERROR.getDescZh(),
                                DN_CANCEL_ERROR.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "交货单入库撤销失败");
                        return ResponseResult.error("交货单入库撤销失败")
                                .add("overseaDn", getDnInfo(sapDeliveryNote))
                                .add("sapReturnedMessage", message)
                                .add("dataToSap", dataToSap);
                    }
                } else {
                    String costCenter = StringUtils.isEmpty(overseaDn.getCostCenter()) ? "" : overseaDn.getCostCenter();
                    List<Map<String, Object>> dataItemsUndercharged = this.baseMapper.dataItemsAnomalyPost(sapDeliveryNote, costCenter);
                    if (dataItemsUndercharged != null && dataItemsUndercharged.size() > 0){
                        /** 存在少收，先撤销少收凭证，再撤销入库凭证 */
                        Map<String, Object> dataHeadAnomaly = this.baseMapper.dataHeadCancelAnomaly(sapDeliveryNote, cancelUnderPostingDate);
                        JSONObject dataToSapCancelAnomaly = iFormatDataToSapService.formatDataToSap(dataHeadAnomaly, null, "Z_SPMS_CANCEL", "SPMS_CANCEL");
                        log.info("=======海外转储收货交货单少收凭证撤销请求SAP dataToSap=========\n" + dataToSapCancelAnomaly);
                        JSONObject messageCancelAnomaly = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(dataToSapCancelAnomaly));
                        log.info("=======海外转储收货交货单少收凭证撤销请求SAP响应结果 message=========\n" + messageCancelAnomaly);
                        JSONObject RETURN_DATA_Anomaly = messageCancelAnomaly.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                        if (SAP_SUCCESS.equals(RETURN_DATA_Anomaly.getString("O_TYPE"))){
                            String cancelUnderVoucherNumber = RETURN_DATA_Anomaly.getString("0_MBLNR");
                            overseaDn.setCancelUnderPostingDate(cancelUnderPostingDate);
                            overseaDn.setCancelUnderVoucherNumber(cancelUnderVoucherNumber);
                            overseaDn.setSpmsStatus(DN_CANCEL_TO_SUBMIT_SAP.getCode());
                            this.baseMapper.update(overseaDn, queryWrapper);

                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_TO_SUBMIT_SAP.getDescZh() + ":" + cancelUnderVoucherNumber,
                                    DN_CANCEL_TO_SUBMIT_SAP.getDescEn(), sapLog, dataToSapCancelAnomaly.toJSONString(), messageCancelAnomaly.toJSONString(), "交货单少收凭证撤销提交SAP");

                            Map<String, Object> dataHead = this.baseMapper.dataHeadCancelMigo(sapDeliveryNote, cancelPostingDate);
                            JSONObject dataToSap = iFormatDataToSapService.formatDataToSap(dataHead, null, "Z_SPMS_CANCEL", "SPMS_CANCEL");
                            log.info("=======海外转储收货交货单入库撤销请求SAP dataToSap=========\n" + dataToSap);
                            JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(dataToSap));
                            log.info("=======海外转储收货交货单入库撤销请求SAP响应结果 message=========\n" + message);
                            JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                            if (SAP_SUCCESS.equals(RETURN_DATA.getString("O_TYPE"))){
                                String cancelVoucherNumber = RETURN_DATA.getString("0_MBLNR");
                                overseaDn.setCancelPostingDate(cancelPostingDate);
                                overseaDn.setCancelVoucherNumber(cancelVoucherNumber);
                                overseaDn.setSpmsStatus(DN_CANCEL_TO_APPROVED.getCode());
                                this.baseMapper.update(overseaDn, queryWrapper);

                                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_TO_APPROVED.getDescZh() + ":" + cancelVoucherNumber,
                                        DN_CANCEL_TO_APPROVED.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "交货单入库撤销提交SAP");
                                return ResponseResult.success("撤销到审批通过，交货单已更新").add("overseaDn", getDnInfo(sapDeliveryNote));
                            } else {
                                overseaDn.setCancelPostingDate(cancelPostingDate);
                                this.baseMapper.update(overseaDn, queryWrapper);

                                iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_ERROR.getDescZh(),
                                        DN_CANCEL_ERROR.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "交货单入库撤销失败");
                                return ResponseResult.error("交货单入库撤销失败")
                                        .add("overseaDn", getDnInfo(sapDeliveryNote))
                                        .add("sapReturnedMessage", message)
                                        .add("dataToSap", dataToSap);
                            }
                        } else {
                            overseaDn.setCancelUnderPostingDate(cancelUnderPostingDate);
                            this.baseMapper.update(overseaDn, queryWrapper);

                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_ERROR.getDescZh(),
                                    DN_CANCEL_ERROR.getDescEn(), sapLog, dataToSapCancelAnomaly.toJSONString(), messageCancelAnomaly.toJSONString(), "交货单少收凭证撤销失败");
                            return ResponseResult.error("交货单少收凭证撤销失败")
                                    .add("overseaDn", getDnInfo(sapDeliveryNote))
                                    .add("sapReturnedMessage", messageCancelAnomaly)
                                    .add("dataToSap", dataToSapCancelAnomaly);
                        }

                    } else {
                        Map<String, Object> dataHead = this.baseMapper.dataHeadCancelMigo(sapDeliveryNote, cancelPostingDate);
                        JSONObject dataToSap = iFormatDataToSapService.formatDataToSap(dataHead, null, "Z_SPMS_CANCEL", "SPMS_CANCEL");
                        log.info("=======海外转储收货交货单入库撤销请求SAP dataToSap=========\n" + dataToSap);
                        JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(dataToSap));
                        log.info("=======海外转储收货交货单入库撤销请求SAP响应结果 message=========\n" + message);
                        JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                        if (SAP_SUCCESS.equals(RETURN_DATA.getString("O_TYPE"))){
                            String cancelVoucherNumber = RETURN_DATA.getString("0_MBLNR");
                            overseaDn.setCancelPostingDate(cancelPostingDate);
                            overseaDn.setCancelVoucherNumber(cancelVoucherNumber);
                            overseaDn.setSpmsStatus(DN_CANCEL_TO_APPROVED.getCode());
                            this.baseMapper.update(overseaDn, queryWrapper);

                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_TO_APPROVED.getDescZh() + ":" + cancelVoucherNumber,
                                    DN_CANCEL_TO_APPROVED.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "交货单入库撤销提交SAP");
                            return ResponseResult.success("撤销到审批通过，交货单已更新").add("overseaDn", getDnInfo(sapDeliveryNote));
                        } else {
                            overseaDn.setCancelPostingDate(cancelPostingDate);
                            this.baseMapper.update(overseaDn, queryWrapper);

                            iOverseaOperateLogService.addOverseaOperateLog(userId, sapDeliveryNote, DN_CANCEL_ERROR.getDescZh(),
                                    DN_CANCEL_ERROR.getDescEn(), sapLog, dataToSap.toJSONString(), message.toJSONString(), "交货单入库撤销失败");
                            return ResponseResult.error("交货单入库撤销失败")
                                    .add("overseaDn", getDnInfo(sapDeliveryNote))
                                    .add("sapReturnedMessage", message)
                                    .add("dataToSap", dataToSap);
                        }
                    }
                }
            }

            return ResponseResult.error("交货单当前状态禁止此操作");
        }catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("交货单撤回失败 Exception").add("error", e.getMessage());
        }
    }

    ResponseResult checkCancel(OverseaDnSapDTO overseaDnSapDTO){
        if (overseaDnSapDTO == null){
            return ResponseResult.error("请求参数错误");
        }
        String sapDeliveryNote = overseaDnSapDTO.getSapDeliveryNote();
        Integer dataVersion = overseaDnSapDTO.getDataVersion();
        String cancelPostingDate = overseaDnSapDTO.getCancelPostingDate();
        String cancelUnderPostingDate = overseaDnSapDTO.getCancelUnderPostingDate();
        if (StringUtils.isEmpty(sapDeliveryNote) || dataVersion == null){
            return ResponseResult.error("请求参数错误");
        }
        OverseaDn overseaDn = getDn(sapDeliveryNote);
        if (overseaDn == null){
            return ResponseResult.error("请求交货单不存在");
        }
        if (!dataVersion.equals(overseaDn.getDataVersion())){
            return ResponseResult.error("请求交货单数据已更新，请刷新后重试");
        }
        String spmsStatus = overseaDn.getSpmsStatus();
        String businessType = overseaDn.getBusinessType();
        List<OverseaDnDtl> overseaDnDtlList = iOverseaDnDtlService.getDnDtls(sapDeliveryNote);
        if (overseaDnDtlList == null || overseaDnDtlList.size() == 0){
            return ResponseResult.error("交货单关联明细行数据为空");
        }
        if (DN_APPROVED.getCode().equals(spmsStatus) || DN_SUBMIT_SAP_ERROR.getCode().equals(spmsStatus)){
            for (OverseaDnDtl overseaDnDtl : overseaDnDtlList){
                String sapDeliveryNoteItem = overseaDnDtl.getSapDeliveryNoteItem();
                String materialNo = overseaDnDtl.getMaterialNo();
                String storageInfo = overseaDnDtl.getStorageInfo();
                double actualQtyBasicUnit = overseaDnDtl.getActualQtyBasicUnit();
                if (actualQtyBasicUnit > 0){
                    if (StringUtils.isEmpty(storageInfo)){
                        return ResponseResult.error("实收数量大于0的行:" + sapDeliveryNoteItem + "，料号:" + materialNo + "储位信息无效");
                    }
                    //storageInfoArray 结构参考：[{"storageUuid":"f17e13b4d9ad4d39ba55548eff544391","qty":100,"storageNo":"C010101"}]
                    JSONArray storageInfoArray = JSONArray.parseArray(storageInfo);
                    for (int i = 0; i < storageInfoArray.size(); i ++){
                        JSONObject storageInfoObj = storageInfoArray.getJSONObject(i);
                        String storageUuid = storageInfoObj.getString("storageUuid");
                        double qty = storageInfoObj.getDoubleValue("qty");
                        String storageNo = storageInfoObj.getString("storageNo");
                        if (qty > 0){
                            WhsStorageInventory whsStorageInventory = iWhsStorageInventoryService.getWhsStorageInventory(storageUuid, materialNo);
                            if (whsStorageInventory == null){
                                return ResponseResult.error("物料号" + materialNo + "在储位" + storageNo + "无可用库存");
                            }
                            if (qty > whsStorageInventory.getAvailableQty()){
                                return ResponseResult.error("物料号" + materialNo + "在储位" + storageNo + "可用库存不足");
                            }
                        }
                    }
                }
            }
        }
        if (DN_SUBMIT_SAP.getCode().equals(spmsStatus) || DN_UNDERCHARGED_TO_SAP_ERROR.getCode().equals(spmsStatus)){
            if (StringUtils.isEmpty(cancelPostingDate)){
                return ResponseResult.error("入库凭证撤销过账日期不能为空");
            }
            if (StringUtils.isEmpty(overseaDn.getSapVoucherNumber())){
                return ResponseResult.error("交货单上入库凭证值为空，无法撤销");
            }
        }
        if (DN_COMPLETED.getCode().equals(spmsStatus)){
            if ("01".equals(businessType) || "03".equals(businessType)){
                if (StringUtils.isEmpty(cancelUnderPostingDate) || StringUtils.isEmpty(cancelPostingDate)){
                    return ResponseResult.error("入库凭证撤销过账日期不能为空");
                }
            }
            if (StringUtils.isEmpty(overseaDn.getSapVoucherNumber())){
                return ResponseResult.error("交货单上入库凭证值为空，无法撤销");
            }
        }
        if (DN_WAITING.getCode().equals(spmsStatus) || DN_DELETE.getCode().equals(spmsStatus)){
            return ResponseResult.error("当前状态禁止此操作");
        }
        return ResponseResult.success();
    }
}


